﻿using System;
using Test30.Core;
using Test30.Util;

namespace Test30.Engine
{
    public class MonsterBehavior : Behavior
    {
        public Monster Monster { get; }

        public bool IsAwake => _isAwake;

        public MonsterBehavior(NotNull<Monster> monster, IPathfinder pathfinder)
        {
            Monster = monster;
            _pathfinder = pathfinder ?? throw new ArgumentNullException($"pathfinder");
        }
        public override Action NextAction()
        {
            Entity target = Monster.Dungeon.Game.Hero;
            Vec distance = target.Position - Monster.Position;

            // wake up or fall asleep
            if (!_isAwake)
            {
                // if close enough to the hero, attempt to wake up
                if ((distance.KingLength < WakeUpDistance) && Rng.OneIn(WakeUpChance))
                {
                    WakeUp();
                }
            }
            else
            {
                // if far enough, try to fall back asleep
                if ((distance.KingLength > FallAsleepDistance) && Rng.OneIn(FallAsleepChance))
                {
                    FallAsleep();
                }
            }

            if (_isAwake)
            {
                // consider performing a move
                foreach (Move move in Monster.Race.Moves)
                {
                    // see if it's possible and the odds match
                    if (move.WillUseMove(Monster, target) && move.ShouldAttempt())
                    {
                        // use this move
                        return move.GetAction(Monster, target);
                    }
                }

                // walk
                Direction direction = _pathfinder.GetDirection(Monster, target);

                return new WalkAction(Monster, direction);
            }

            // otherwise, just stand still
            return new WalkAction(Monster, Direction.None);
        }

        public override void Disturb()
        {
            WakeUp();
        }

        private void WakeUp()
        {
            _isAwake = true;
        }

        protected bool IsOccupiedByOtherMonster(Vec move)
        {
            Entity occupier = Monster.Dungeon.Entities.GetAt(Monster.Position + move);

            if ((occupier != null) && (occupier != Monster) && (occupier != Monster.Dungeon.Game.Hero))
            {
                return true;
            }

            return false;
        }

        private void FallAsleep()
        {
            _isAwake = false;
        }

        private const int WakeUpDistance = 8;
        private const int WakeUpChance = 2;

        private const int FallAsleepDistance = 24;
        private const int FallAsleepChance = 4;

        private bool _isAwake;

        private readonly IPathfinder _pathfinder;
    }
}
